﻿#include "codeeditor.h"

CodeEditor::CodeEditor(QWidget *parent):QPlainTextEdit(parent)
{
    setMouseTracking(true);//获取鼠标移动
    setWordWrapMode(QTextOption::NoWrap);//自动换行, 会导致获取的行号有问题
    setTabStopDistance(32);
    backgroundColor = QColor(240,240,240);
    fontColor = QColor(160,160,160);
    fontCurrentColor = QColor(0,0,0);
    lineNumberPositionX = 0;
    tagCurrentWidth = 0;
    lineNumberWidth = 0;
    spacing = 5;
    bLineNumber = false;
    bHightCurrentLineNumber = true;

    tagLineNumberArea = new TagLineNumberArea(this);

    connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
    connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea);

    //connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::hightlightCurrentLine);

    UpdateLineNumberWidth(0);
}

CodeEditor::~CodeEditor()
{

}

void CodeEditor::PaintTag(QPainter &painter, int line, int y)
{
    for (TagDataItem* item:vecTagDataItem)
    {
        switch (item->tagType)
        {
        case NUMBER:
            break;
        case MARKROUND:
            if(item->BLineNumbersMark(line))
            {
                painter.setPen(QColor(item->getLineColor(line)));
                painter.setBrush(QColor(item->getLineColor(line)));
                int w = item->width < fontMetrics().height() ? item->width : fontMetrics().height();
                painter.drawEllipse(item->positionX + item->width/2 - w/2, y + fontMetrics().height()/2 - w/2,
                                    w, w);
            }
            break;
        case MARKBLOCK:
            if(item->BLineNumbersMark(line))
            {
                painter.setPen(QColor(item->getLineColor(line)));
                painter.setBrush(QColor(item->getLineColor(line)));
                painter.drawRect(item->positionX, y, item->width, fontMetrics().height());
            }
            break;
        default:
            break;
        }
    }
}


void CodeEditor::AddTag(TagDataItem *item)
{
    item->positionX = tagCurrentWidth + spacing;
    vecTagDataItem.push_back(item);
    tagCurrentWidth = tagCurrentWidth + spacing + item->width;
    if(!bLineNumber && item->tagType == NUMBER)
    {
        lineNumberPositionX = item->positionX;
        lineNumberWidth = item->width;
        bLineNumber = true;
    }
}

void CodeEditor::UpdateColor(int line, int startIndex, int endIndex, QColor fontColor, QColor backColor)
{
    QTextCursor cursor = this->textCursor();

    //移动行
    cursor.movePosition(QTextCursor::Start);
    for(int i=1; i<line; i++)
    {
        cursor.movePosition(QTextCursor::Down);
    }
    //移动列
    cursor.movePosition(QTextCursor::StartOfBlock);
    for(int i=1; i<startIndex; i++)
    {
        cursor.movePosition(QTextCursor::NextCharacter);
    }
    if(endIndex == 0)
    {
        cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    }
    else
    {
        for(int i=1; i<endIndex; i++)
        {
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
        }
    }
    QTextCharFormat fmt;
    fmt.setForeground(QBrush(fontColor));//字体色
    fmt.setBackground(QBrush(backColor));//背景色
    cursor.setCharFormat(fmt);
}

void CodeEditor::UpdateColor(int startLine, int endLine, int startColumn, int endColumn, QColor fontColor, QColor backColor)
{
    QTextCursor cursor = this->textCursor();

    //移动行
    cursor.movePosition(QTextCursor::Start);
    for(int i=1; i<startLine; i++)
    {
        cursor.movePosition(QTextCursor::Down);
    }
    //移动列
    cursor.movePosition(QTextCursor::StartOfBlock);
    for(int i=1; i<startColumn; i++)
    {
        cursor.movePosition(QTextCursor::NextCharacter);
    }
    //移动行
    cursor.movePosition(QTextCursor::Start);
    for(int i=startLine; i<endLine; i++)
    {
        cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
    }
    //移动列
    cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
    for(int i=0; i<endColumn; i++)
    {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
    }

    QTextCharFormat fmt;
    fmt.setForeground(QBrush(fontColor));//字体色
    fmt.setBackground(QBrush(backColor));//背景色
    cursor.setCharFormat(fmt);
}

void CodeEditor::UpdateLineNumberWidth(int lineNumberWidthNew)
{
    int width = tagCurrentWidth - lineNumberWidth + lineNumberWidthNew;
    tagCurrentWidth = width;
    UpdateTagWidth(lineNumberWidth, lineNumberWidthNew);
    lineNumberWidth = lineNumberWidthNew;
}

void CodeEditor::UpdateTagWidth(int lineNumberWidthOld, int lineNumberWidth)
{
    bool numberAfterTag = false;
    for(TagDataItem* item: vecTagDataItem)
    {
        if(item->tagType == NUMBER)
        {
            numberAfterTag = true;
        }

        if(numberAfterTag)
        {
            item->positionX = item->positionX - lineNumberWidthOld + lineNumberWidth;
        }
    }
}

void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
{
    setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}

void CodeEditor::hightlightCurrentLine()
{
    QList<QTextEdit::ExtraSelection> extraSelections;
    if(!isReadOnly())
    {
        QTextEdit::ExtraSelection selection;
        QColor lineColor = QColor(Qt::yellow).lighter(160);
        selection.format.setBackground(lineColor);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = textCursor();
        selection.cursor.clearSelection();
        extraSelections.append(selection);
    }
    setExtraSelections(extraSelections);
}

void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
{
    if (dy)
        tagLineNumberArea->scroll(0, dy);
    else
        tagLineNumberArea->update(0, rect.y(), tagLineNumberArea->width(), rect.height());

    if (rect.contains(viewport()->rect()))
        updateLineNumberAreaWidth(0);
}

void CodeEditor::updateLineNumber()
{
    tagLineNumberArea->update();
}

void CodeEditor::GoToLine(int line)
{
    QTextCursor textCursor = this->textCursor();
    int position = document()->findBlockByNumber(line - 1).position();
    textCursor.setPosition(position, QTextCursor::MoveAnchor);
    setTextCursor(textCursor);
    this->verticalScrollBar()->setValue(line-1);
}

int CodeEditor::GetLineNumber(QTextCursor &cursor)
{
    QTextLayout *layout = cursor.block().layout();
    int pos = cursor.position() - cursor.block().position();
    int line = layout->lineForTextPosition(pos).lineNumber() + cursor.block().firstLineNumber();
    return line;
}

int CodeEditor::lineNumberAreaWidth()
{
    if(!bLineNumber)
        return tagCurrentWidth;
    int digits = 1;
    int max = qMax(1, blockCount());
    while(max >= 10)
    {
        max /=10;
        ++digits;
    }
    int lineNumberWidth = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9'))*digits;
    UpdateLineNumberWidth(lineNumberWidth);
    return tagCurrentWidth;
}

void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
    //当前行
    QTextCursor cursor = textCursor();
    int line = GetLineNumber(cursor);
    currentLineNumber = line;
    currentLinePositionY = -1;
    //背景
    QPainter painter(tagLineNumberArea);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.fillRect(event->rect(), backgroundColor);

    //
    QTextBlock block = firstVisibleBlock();
    int blockNumber = block.blockNumber();
    int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top());
    int bottom = top + qRound(blockBoundingRect(block).height());

    while(block.isValid() && top <= event->rect().bottom())
    {
        if(block.isVisible() && bottom >= event->rect().top())
        {
            QString number = QString::number(blockNumber +1);
            if(blockNumber == line)
                currentLinePositionY = top;//记录当前位置
            //记载行号
            if(bLineNumber)
            {
                if(blockNumber == line && bHightCurrentLineNumber)
                {
                    painter.setPen(fontCurrentColor);//当前行号高亮
                    QFont ft = painter.font();
                    ft.setBold(true);
                    painter.setFont(ft);
                }
                else
                {
                    painter.setPen(fontColor);
                }
                painter.drawText(lineNumberPositionX, top, lineNumberWidth, fontMetrics().height(),
                                 Qt::AlignRight, number);
            }
            //绘制tag
            PaintTag(painter, blockNumber, top);
        }
        block = block.next();
        top = bottom;
        bottom = top + qRound(blockBoundingGeometry(block).height());
        ++blockNumber;
    }
}

void CodeEditor::resizeEvent(QResizeEvent *e)
{
    QPlainTextEdit::resizeEvent(e);

    QRect cr = contentsRect();
    tagLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}

QString GetQString(QByteArray byteArray)
{
    QTextCodec::ConverterState state;
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QString qstr = codec->toUnicode(byteArray.constData(), byteArray.size(), &state);
    if(state.invalidChars > 0)
        qstr = QTextCodec::codecForName("GBK")->toUnicode(byteArray);
    else {
        qstr = byteArray;
    }
    return qstr;
}
